package org.word.service.impl;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.web.client.RestTemplate;
import org.word.dto.Request;
import org.word.dto.Response;
import org.word.dto.Table;
import org.word.service.WordService;
import org.word.utils.JsonUtils;
import org.word.utils.MenuUtils;

import java.io.IOException;
import java.util.*;

/**
 * Created by XiuYin.Cui on 2018/1/12.
 */
@Slf4j
@Service
public class WordServiceImpl implements WordService {

    @Autowired
    private RestTemplate restTemplate;

    @Value("${swagger.url}")
    private String swaggerUrl;

    public static final String PATHS = "paths";
    public static final String TAGS = "tags";
    public static final String SUMMARY = "summary";
    public static final String CONSUMES = "consumes";
    public static final String PRODUCES = "produces";
    public static final String PARAMETERS = "parameters";
    public static final String RESPONSES = "responses";
    public static final String SPLIT_01 = ",";
    public static final String $REF = "$ref";
    public static final String DESCRIPTION = "description";
    public static final String DEFINITIONS = "definitions";
    public static final String TYPE = "type";
    public static final String NAME = "name";
    public static final String SCHEMA = "schema";
    public static final String ITEMS = "items";

    @Override
    public List<Table> tableList(String reqUrl) {
        List<Table> result = new ArrayList<>();
        try {
            String jsonStr = "";
            if(StringUtils.isBlank(reqUrl)){
                jsonStr = restTemplate.getForObject(swaggerUrl, String.class);
            }else {
                jsonStr = restTemplate.getForObject(reqUrl, String.class);
            }
            // convert JSON string to Map
            Map<String, Object> map = JsonUtils.readValue(jsonStr, HashMap.class);
            //解析paths
            Map<String, Object> paths = (Map<String,Object>) map.get(PATHS);
            if (paths == null) {
                return result;
            }
            Iterator<Map.Entry<String, Object>> it = paths.entrySet().iterator();
            while (it.hasNext()) {
                Map.Entry<String, Object> path = it.next();
                //封装Table
                Table table = new Table();
                // 1.封装请求路径
                table.setUrl(path.getKey());
                // 解析内容
                Map<String, Object> value = (Map<String, Object>)path.getValue();
                // 2.封装请求方式，类似为 get,post,delete,put 这样
                table.setRequestType(getRequestType(value));

                //不管有几种请求方式，都只解析第一种
                Iterator<Map.Entry<String, Object>> valueIter = value.entrySet().iterator();
                Map.Entry<String, Object> firstRequest = valueIter.next();
                Map<String, Object> content = (Map<String, Object>)firstRequest.getValue();
                // 3. 拼装大标题（类说明）
                String title = String.valueOf(((List) content.get(TAGS)).get(0));
                //是否添加为菜单
                if (MenuUtils.isMenu(title)) {
                    table.setTitle(title);
                }
                // 4.拼装小标题 （方法说明 接口描述）
                String tag = String.valueOf(content.get(SUMMARY));
                table.setTag(tag);
                table.setDescription(tag);
                // 5.请求参数格式，类似于 multipart/form-data
                table.setRequestForm(handlerParameterFormatter(content, CONSUMES));
                // 6.返回参数格式，类似于 application/json
                table.setResponseForm(handlerParameterFormatter(content, PRODUCES));
                // 7. 拼装请求体与请求参数
                List<Request> requestList = handlerParameters((ArrayList) content.get(PARAMETERS));
                table.setRequestList(requestList);
                table.setRequestParam(JsonUtils.writeJsonStr(buildParamMap(requestList, map)));
                // 8.响应列表
                Map<String, Object> responses = (LinkedHashMap) content.get(RESPONSES);
                List<Response> responseList = handlerResponse(responses);
                table.setResponseList(responseList);
                // 9.处理响应参数
                String str = handlerResponseParameter(map, responses);
                table.setResponseParam(str);
                result.add(table);
            }
        } catch (Exception e) {
            log.error("parse error", e);
        }
        return result;
    }

    @Override
    public List<Table> tableList() {
        return tableList(null);
    }

    /**
     * 拼装请求类型
     *
     * @param value
     * @return
     */
    private String getRequestType(Map<String, Object> value) {
        String requestType = "";
        Set<String> requestTypes = value.keySet();
        for (String str : requestTypes) {
            requestType += str + SPLIT_01;
        }
        if(StringUtils.isBlank(requestType)){
            return requestType;
        }
        return StringUtils.removeEnd(requestType, SPLIT_01);
    }

    /**
     * 处理返回参数
     * @param map
     * @param responses
     * @return
     */
    private String  handlerResponseParameter(Map<String, Object> map, Map<String, Object> responses) {
        // 取出来状态是200时的返回值
        Object obj = responses.get("200");
        if (obj == null) {
            return "";
        }
        Object schema = ((Map) obj).get(SCHEMA);
        if (((Map) schema).get($REF) != null) {
            //非数组类型返回值
            String ref = (String) ((Map) schema).get($REF);
            //解析swagger2 ref链接
            ObjectNode objectNode = parseRef(ref, map);
            return objectNode.toString();
        }
        Object items = ((Map) schema).get(ITEMS);
        if (items != null && ((Map) items).get($REF) != null) {
            //数组类型返回值
            String ref = (String) ((Map) items).get($REF);
            //解析swagger2 ref链接
            ObjectNode objectNode = parseRef(ref, map);
            ArrayNode arrayNode = JsonUtils.createArrayNode();
            arrayNode.add(objectNode);
            return arrayNode.toString();
        }
        return "";
    }

    /**
     * 处理参数格式
     * @param content
     * @param produces2
     * @return
     */
    private String handlerParameterFormatter(Map<String, Object> content, String produces2) {
        String temp = "";
        List<String> produces = (List) content.get(produces2);
        if (produces != null && produces.size() > 0) {
            for (String produce : produces) {
                temp += produce + SPLIT_01;
            }
        }
        if(StringUtils.isBlank(temp)){
            return temp;
        }
        return StringUtils.removeEnd(temp, SPLIT_01);
    }

    /**
     * 处理请求参数内容
     * @param parameters
     * @return
     */
    private List<Request> handlerParameters(List<LinkedHashMap> parameters) {
        List<Request> requestList = new ArrayList<>();
        if (CollectionUtils.isEmpty(parameters)) {
            return requestList;
        }
        for (Map<String, Object> param : parameters) {
            Request request = new Request();
            request.setName(String.valueOf(param.get(NAME)));
            Object in = param.get("in");
            if (in != null && "body".equals(in)) {
                request.setType(String.valueOf(in));
                Map<String, Object> schema = (Map) param.get(SCHEMA);
                Object ref = schema.get($REF);
                // 数组情况另外处理
                if (schema.get(TYPE) != null && "array".equals(schema.get(TYPE))) {
                    ref = ((Map) schema.get(ITEMS)).get($REF);
                }
                request.setParamType(ref == null ? "{}" : ref.toString());
            } else {
                request.setType(param.get(TYPE) == null ? "Object" : param.get(TYPE).toString());
                request.setParamType(String.valueOf(in));
            }
            request.setRequire((Boolean) param.get("required"));
            request.setRemark(String.valueOf(param.get(DESCRIPTION)));
            requestList.add(request);
        }
        return requestList;
    }

    /**
     * 处理响应状态码
     * @param responses
     * @return
     */
    private List<Response> handlerResponse(Map<String, Object> responses) {
        Iterator<Map.Entry<String, Object>> it3 = responses.entrySet().iterator();
        List<Response> responseList = new ArrayList<>();
        while (it3.hasNext()) {
            Response response = new Response();
            Map.Entry<String, Object> entry = it3.next();
            // 状态码 200 201 401 403 404 这样
            response.setName(entry.getKey());
            LinkedHashMap<String, Object> statusCodeInfo = (LinkedHashMap) entry.getValue();
            response.setDescription(String.valueOf(statusCodeInfo.get(DESCRIPTION)));
            response.setRemark(String.valueOf(statusCodeInfo.get(DESCRIPTION)));
            responseList.add(response);
        }
        return responseList;
    }

    /**
     * 从map中解析出指定的ref
     *
     * @param ref ref链接 例如："#/definitions/PageInfoBT«Customer»"
     * @param ref ref链接 例如："#/definitions/分类对象"
     * @param map 是整个swagger json转成map对象
     * @return
     * @author fpzhan
     */
    private ObjectNode parseRef(String ref, Map<String, Object> map) {
        ObjectNode objectNode = JsonUtils.createObjectNode();
        //取出ref最后一个参数 start
        String lastRefStart = getLastRefStart(ref);
        if (StringUtils.isBlank(lastRefStart)) {
            return objectNode;
        }
        Map<String, Object> definitions = (Map<String, Object>) map.get(DEFINITIONS);
        Map<String, Object> tmpMap = (Map<String, Object>) definitions.get(lastRefStart);
        if (tmpMap == null) {
            return objectNode;
        }
        //取出参数
        Map<String, Object> properties = (Map<String, Object>) tmpMap.get("properties");
        if (properties == null) {
            return objectNode;
        }
        Set<String> keys = properties.keySet();
        //遍历key
        for (String key : keys) {
            Map<String, Object> keyMap = (Map) properties.get(key);
            if ("array".equals(keyMap.get(TYPE))) {
                //数组的处理方式
                String sonRef = (String) ((Map) keyMap.get(ITEMS)).get($REF);
                // 处理循环创建问题
                String refStart = getLastRefStart(sonRef);
                if (lastRefStart.equals(refStart)) {
                    return objectNode;
                }
                JsonNode jsonNode = parseRef(sonRef, map);
                ArrayNode arrayNode = JsonUtils.createArrayNode();
                arrayNode.add(jsonNode);
                objectNode.set(key, arrayNode);
            } else if (keyMap.get($REF) != null) {
                //对象的处理方式
                String sonRef = (String) keyMap.get($REF);
                ObjectNode object = parseRef(sonRef, map);
                objectNode.set(key, object);
            } else {
                //其他参数的处理方式，string、int
                String str = "";
                if (keyMap.get(DESCRIPTION) != null) {
                    str = str + keyMap.get(DESCRIPTION);
                }
                if (keyMap.get("format") != null) {
                    str = str + String.format("格式为(%s)", keyMap.get("format"));
                }
                objectNode.put(key, str);
            }
        }
        return objectNode;
    }

    /**
     * 拿到最后一个ref Start
     * @param sonRef
     * @return
     */
    private String getLastRefStart(String sonRef) {
        if (StringUtils.isBlank(sonRef) || !sonRef.startsWith("#")) {
            return "";
        }
        String[] split = sonRef.split("/");
        if (split.length == 0) {
            return "";
        }
        return split[split.length - 1];
    }

    /**
     * 封装参数类型
     * @param list
     * @param map
     * @return
     */
    private Map<String, Object> buildParamMap(List<Request> list, Map<String, Object> map) throws IOException {
        Map<String, Object> paramMap = new HashMap<>(8);
        if (list == null || list.size() == 0) {
            return paramMap;
        }
        for (Request request : list) {
            String name = request.getName();
            String type = request.getType();
            switch (type) {
                case "string":
                    paramMap.put(name, "string");
                    break;
                case "integer":
                    paramMap.put(name, 0);
                    break;
                case "number":
                    paramMap.put(name, 0.0);
                    break;
                case "boolean":
                    paramMap.put(name, true);
                    break;
                case "body":
                    String paramType = request.getParamType();
                    ObjectNode objectNode = parseRef(paramType, map);
                    return JsonUtils.readValue(objectNode.toString(), Map.class);
                default:
                    paramMap.put(name, null);
                    break;
            }
        }
        return paramMap;
    }
}
